﻿using System.Text;

namespace BigfootMVC.Helpers
{

    /// <summary>
    /// This is a simple class to aid us in the creation of jQuery scripts
    /// </summary>
    public class JQHelper
    {

        private readonly StringBuilder builder = new StringBuilder();

        /// <summary>
        /// Determines if the script is empty, meaning no commands have been added to it
        /// </summary>
        public bool IsEmpty()
        {
            return builder.ToString() == string.Empty;
        }

        /// <summary>
        /// Factory method to create a new JQHelper
        /// </summary>
        public static JQHelper Create()
        {
            return new JQHelper();
        }

        /// <summary>
        /// Selects a set of elements using a jquery selector. If the JQHelper has other 
        /// commands and the script is not ended with a ; it adds it before adding this statement
        /// Appends: [;]jQuery('selector')
        /// </summary>
        /// <param name="selector">Any jQuery selector</param>
        public JQHelper Select(string selector)
        {
            ClosePreviousStatement();
            builder.AppendFormat("jQuery('{0}')", selector);
            return this;
        }

        /// <summary>
        /// Adds a ; if needed to the previous statement
        /// </summary>
        private void ClosePreviousStatement()
        {
            if (IsEmpty() == false && builder.ToString().TrimEnd().EndsWith(";") == false)
            {
                builder.Append(";");
            }
        }

        /// <summary>
        /// Selects a set of elements using a jquery element id selector. If the JQHelper has other 
        /// commands and the script is not ended with a ; it adds it before adding this statement
        /// Appends: [;]jQuery('#selector')
        /// </summary>
        /// <param name="selector">And element id selector with or without the #</param>
        public JQHelper SelectID(string selector)
        {
            return Select(GetElementID(selector));
        }

        /// <summary>
        /// Selects all input elements within the contianer except file inputs. If the JQHelper has other 
        /// commands and the script is not ended with a ; it adds it before adding this statement
        /// Appends: [;]jQuery('#container :input, #container select, #container textarea')
        /// </summary>
        /// <param name="selector">The selector containing the fields. This could any jquery selector</param>
        public JQHelper SelectFields(string selector)
        {
            return Select(string.Format("{0} :input, {0} select, {0} textarea", selector));
        }


        /// <summary>
        /// Selects the parent form of the executing element. If the JQHelper has other 
        /// commands and the script is not ended with a ; it adds it before adding this statement
        /// Appends: [;]jQuery(this).parents('form:eq[0]')
        /// </summary>
        public JQHelper SelectParentForm()
        {
            ClosePreviousStatement();
            builder.Append("jQuery(this).parents('form:eq(0)')");
            return this;
        }



        /// <summary>
        /// Seriliazes the fields within the selection
        /// Appends: [.]fieldSerialize()
        /// </summary>
        public JQHelper fieldSerialize()
        {
            return AddCommand("fieldSerialize()");
        }

        /// <summary>
        /// Adds a class to the select element(s). 
        /// Appends: [.]addClass('className')
        /// </summary>
        /// <param name="className">Class name to add</param>
        public JQHelper addClass(string className)
        {
            return AddCommand("addClass({0})", GetString(className));
        }

        /// <summary>
        /// Removes a class from the select element(s). 
        /// Appends: [.]removeClass('className')
        /// </summary>
        /// <param name="className">Class name to add</param>
        public JQHelper removeClass(string className)
        {
            return AddCommand("removeClass({0})", GetString(className));
        }

        /// <summary>
        /// Removes the matched elements. 
        /// Appends: [.]remove()
        /// </summary>
        public JQHelper remove()
        {
            return AddCommand("remove()");
        }

        /// <summary>
        /// Clears the fields matched by the specified selector. 
        /// Appends: [.]clearFields()
        /// </summary>
        public JQHelper clearFields()
        {
            return AddCommand("clearFields()");
        }

        /// <summary>
        /// Shows the selected element(s)
        /// Appends: [.]show()
        /// </summary>
        public JQHelper show()
        {
            return AddCommand("show()");
        }

        /// <summary>
        /// Hides the selected element(s)
        /// Appends: [.]hide()
        /// </summary>
        public JQHelper hide()
        {
            return AddCommand("hide()");
        }

        /// <summary>
        /// Toggles the visiblity of the selected element(s)
        /// Appends: [.]toggle()
        /// </summary>
        public JQHelper toggle()
        {
            return AddCommand("toggle()");
        }

        /// <summary>
        /// Adds or removes a class when not found from the selected element(s)
        /// Appends: [.]toggleClass('className')
        /// </summary>
        public JQHelper toggleClass(string className)
        {
            return AddCommand("toggleClass(" + JSBuilder.GetString(className) + ")");
        }

        /// <summary>
        /// Removes the html conents and hides the selected element(s)
        /// Appends: [.]html('').hide()
        /// </summary>
        public JQHelper clear()
        {
            AddCommand("html('')");
            AddCommand("hide()");
            return this;
        }

        /// <summary>
        /// Removes the value of the field
        /// Appends: [.]val('')
        /// </summary>
        public JQHelper clearVal()
        {
            return AddCommand("val('')");
        }

        /// <summary>
        /// Removes the value of the field
        /// Appends: [.]val('')
        /// </summary>
        public JQHelper val()
        {
            return AddCommand("val()");
        }

        /// <summary>
        /// Calls the trigger function and triggers a certain event
        /// Appends: [.]trigger('trigger')
        /// </summary>
        public JQHelper trigger(string trigger)
        {
            AddCommand("trigger('" + trigger + "')");
            return this;
        }

        /// <summary>
        /// Calls the ajaxSubmit with the specified url and updates the specified element with the returned value
        /// url passed in will be properly escaped and quoted, so you don't have to clean it up
        /// Appends: [.]ajaxSubmit({ url: '[url]', target: '#[updateElement]' })
        /// </summary>
        public JQHelper ajaxSubmit(string url, string updateElement)
        {
            var options = new JSObject();
            options.AddString("url", url).AddString("target", GetElementID(updateElement));
            return ajaxSubmit(options);
        }

        /// <summary>
        /// Calls the ajaxSubmit with the specified url and updates the specified element with the returned value. 
        /// It also appends the other options if specified.
        /// url passed in will be properly escaped and quoted, so you don't have to clean it up
        /// Appends: [.]ajaxSubmit({ url: '[url]', target: '#[updateElement]' [,[otherOptions]]})
        /// </summary>
        public JQHelper ajaxSubmit(string url, string updateElement, JSObject otherOptions)
        {
            var options = new JSObject();
            options.AddString("url", url).AddString("target", GetElementID(updateElement));
            if (otherOptions != null)
                options.Merge(otherOptions);
            
            return ajaxSubmit(options);
        }

        /// <summary>
        /// Calls the ajaxSubmit with the specified set of options
        /// JSBuilder.GetString will be called on the url passed in, so you don't have to clean it up
        /// Appends: [.]ajaxSubmit({ url: '[url]', target: '#[updateElement]' [,[otherOptions]]})
        /// </summary>
        public JQHelper ajaxSubmit(JSObject options)
        {
            return AddCommand("ajaxSubmit(" + options + ")");
        }

        //JQ.SelectID(formId).AddCommand("ajaxSubmit({ target: '#" + updatePanel + "', url: " + postUrl + "})").ToString()

        /// <summary>
        /// Calls the html function and sets it to the expression
        /// Appends: [.]html(value)
        /// </summary>
        public JQHelper html(string expression)
        {
            AddCommand("html("+ expression + ")");
            return this;
        }

        /// <summary>
        /// Loads the content of the url
        /// Appends: [.]load('url')
        /// </summary>
        /// <param name="url">The url to load unescaped</param>
        public JQHelper load(string url)
        {
            AddCommand("load(" + GetString(url) + ")");
            return this;
        }

        /// <summary>
        /// Loads the content of the url
        /// Appends: [.]load('url', callback)
        /// </summary>
        /// <returns></returns>
        public JQHelper load(string url, string callback)
        {
            AddCommand("load(" + GetString(url) + ", " + callback + ")");
            return this;
        }

        /// <summary>
        /// Set the focus for the selected element
        /// Appends: [.]focus()
        /// </summary>
        public JQHelper focus()
        {
            return AddCommand("focus()");
        }

        /// <summary>
        /// Runs the provided javascript for each matched element
        /// Appends: [.]each(function(i, item) { [Your Javascript] });
        /// </summary>
        public JQHelper each(string javascript)
        {
            return AddCommand("each(function(i, item){ " + javascript + " })");
        }

        /// <summary>
        /// Set the focus for the specified element
        /// Appends: [.]focus()
        /// </summary>
        public JQHelper focus(string elementId)
        {
            return SelectID(elementId).focus();
        }

        /// <summary>
        /// Serializes the form into an array to be used for posting
        /// Appends: [.]formToArray()
        /// </summary>
        /// <returns></returns>
        public JQHelper formToArray()
        {
            return AddCommand("formToArray()");
        }
                
        /// <summary>
        /// Adds a command to the selected element(s). It uses the String.Format to merge command and possible parameters
        /// Appends: [.]command('with possible values')
        /// i.e. AddCommand("addClass('{0}')", "class1 class2") appends [.]addClass('class1 class2')
        /// </summary>
        /// <param name="statement">The command name with possible String.Format {} braces to merge with the params</param>
        /// <param name="params">Values to merge with the command. When left empty then nothing is merged</param>
        public JQHelper AddCommand(string statement, params string[] @params)
        {
            if (builder.ToString().EndsWith(".") == false)
                builder.Append(".");
            if (@params != null && @params.Length > 0)
            {
                builder.AppendFormat(statement, @params);
            }
            else
            {
                builder.Append(statement);
            }
            return this;
        }

        /// <summary>
        /// Returns a scaped and properly quoted string for inclussion in javascript
        /// </summary>
        /// <param name="Data">The string to escape and quote</param>
        private static string GetString(string Data)
        {
            return JSBuilder.GetString(Data);
        }

        /// <summary>
        /// Retrives an element id jquery selector. It adds the # at the beggining of the element id if not there
        /// </summary>
        public static string GetElementID(string elementid)
        {
            elementid = elementid.Trim();
            if (elementid.StartsWith("#") == false)
            {
                elementid = "#" + elementid;
            }
            return elementid;
        }

        /// <summary>
        /// Outpus the script ready for inclussion in a page. Does not output the script tag or the jquery on ready by default.
        /// it also does not add a semicolon at the end by default
        /// </summary>
        public override string ToString()
        {
            return ToString(false);
        }

        /// <summary>
        /// Outpus the script ready for inclussion in a page. Does not output the script tag or the jquery on ready by default.
        /// it also adds a semicolon at the end if requted and it isn't there
        /// </summary>
        public string ToString(bool AddSemicolonAtTheEnd)
        {
            if (AddSemicolonAtTheEnd && builder.ToString().Trim().EndsWith(";") == false)
            {
                builder.Append(";");
            }
            return builder.ToString();
        }

        public string ToScriptBlock()
        {
            var script = new StringBuilder();
            script.Append(" <script type='text/javascript'> ");
            script.Append("jQuery(document).ready(function(){ ");
            script.Append("   " + ToString(true));
            script.Append(" }); ");
            script.Append(" </script>");
            return script.ToString();
        }

    }
}
